/*
 * =====================================================================================
 *
 *       Filename:  block.c
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  2025/03/18 23时12分34秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  zhuzonghui(puchuchen@outlook.com) (),
 *   Organization:
 *
 * =====================================================================================
 */
#include <fuquOS/block.h>
#include <fuquOS/io.h>
#include <fuquOS/memory.h>
#include <fuquOS/printk.h>
#include <fuquOS/string.h>

void insw(u16 port, u16 *buffer, u32 count)
{
    for (u32 i = 0; i < count; i++)
    {
        buffer[i] = inw(port);
    }
}

u16 ata_identify_buffer[256];

int ata_identify()
{
    // 选择主盘
    outb(0x1F6, 0xA0);

    // 发送IDENTIFY命令
    outb(0x1F7, ATA_COMMAND_IDENTIFY);

    // 检测硬盘是否响应
    if (inb(ATA_PRIMARY_STATUS) == 0)
    {
        return -1; // 没有硬盘
    }

    while (inb(ATA_PRIMARY_STATUS) & 0x80)
        ; // 等待BSY清零
    while (!(inb(ATA_PRIMARY_STATUS) & 0x08))
        ; // 等待DRQ置位

    // 读取512字节信息
    insw(ATA_PRIMARY_IO, ata_identify_buffer, 256);

    return 0;
}

// 解析字符串(ATA返回的是字节反转的)
void ata_string_fix(u16 *src, char *dst, int len)
{
    for (int i = 0; i < len / 2; i++)
    {
        dst[i * 2] = src[i] >> 8;
        dst[i * 2 + 1] = src[i] & 0xFF;
    }
    dst[len] = 0; // 结束符
}

u64 disk_size = 0;
// 解析IDENTIFY
void ata_parse_identify()
{
    char serial[21], model[41];

    ata_string_fix(&ata_identify_buffer[10], serial, 20);
    ata_string_fix(&ata_identify_buffer[27], model, 20);

    u32 sectors = *(u32 *)&ata_identify_buffer[100];
    disk_size = (u64)sectors * 512 / (1024 * 1024); // MB

    printk("Block model: %s\n", model);
    printk("Block serial: %s\n", serial);
    printk("Block disk size: %llx MB\n", disk_size);

    // 判断是否支持LBA
    if (ata_identify_buffer[49] & (1 << 9))
    {
        printk("Block support LBA & CHS\n");
    }
    else
    {
        printk("Block support CHS only\n");
    }
}
// 查看磁盘扇区大小
void ata_check_sector_size()
{
    if (ata_identify_buffer[106] & (1 << 14))
    {
        printk("Block sector size 4096\n");
    }
    else
    {
        printk("Block sector size 512\n");
    }
}
// 查看磁盘的控制器模式
void ata_check_pio_dma()
{
    if (ata_identify_buffer[49] & (1 << 8))
    {
        printk("Block support PIO\n");
    }
    if (ata_identify_buffer[49] & (1 << 10))
    {
        printk("Block support DMA\n");
    }
}

MBR *mbr;

// 查看分区信息
void read_mbr()
{
    ata_read_sector(0, mbr);

    if (mbr->signature != 0xAA55)
    {
        printk_err("Block is not MBR");
        return;
    }

    for (int i = 0; i < 4; i++)
    {
        PartitionEntry *part = &(mbr->partitions[i]);
        if (part->type == 0)
        {
            continue;
        }

        printk("Block %d type %x,start LBA %d,size %d MB\n", i, part->type, part->lba_start, part->sectors / 2048);
    }
}

// 读取一个扇区到buffer中
int ata_read_sector(u32 lba, void *buffer)
{
    // 1. 选择主盘 + LBA 模式
    outb(0x1F6, 0xE0 | ((lba >> 24) & 0x0F));

    // 2. 发送要读取的扇区数（1 扇区）
    outb(0x1F2, 1);

    // 3. 发送 LBA 地址（低 24 位）
    outb(0x1F3, (u8)(lba));
    outb(0x1F4, (u8)(lba >> 8));
    outb(0x1F5, (u8)(lba >> 16));

    // 4. 发送 READ SECTORS 命令
    outb(0x1F7, ATA_COMMAND_READ);

    // 5. 等待硬盘准备好数据
    while (inb(ATA_PRIMARY_STATUS) & ATA_SR_BSY)
        ; // 等待 BSY 清零
    while (!(inb(ATA_PRIMARY_STATUS) & ATA_SR_DRQ))
        ; // 等待 DRQ 置位

    // 6. 读取 512 字节数据
    insw(ATA_PRIMARY_IO, buffer, 256);

    return 0;
}

void block_init()
{
    ata_identify();
    ata_parse_identify();
    ata_check_sector_size();
    ata_check_pio_dma();
    read_mbr();
}

void read_disk(u32 lba, u16 *buffer)
{
    // 1. 选择主盘 + LBA 模式
    outb(0x1F6, 0xE0 | ((lba >> 24) & 0x0F));

    // 2. 发送要读取的扇区数（8 扇区）
    outb(0x1F2, 8);

    // 3. 发送 LBA 地址（低 24 位）
    outb(0x1F3, (u8)(lba));
    outb(0x1F4, (u8)(lba >> 8));
    outb(0x1F5, (u8)(lba >> 16));

    // 4. 发送 READ SECTORS 命令
    outb(0x1F7, ATA_COMMAND_READ);

    // 5. 等待硬盘准备好数据
    while (inb(ATA_PRIMARY_STATUS) & ATA_SR_BSY)
        ; // 等待 BSY 清零
    while (!(inb(ATA_PRIMARY_STATUS) & ATA_SR_DRQ))
        ; // 等待 DRQ 置位


    insw(ATA_PRIMARY_IO, buffer, 2048);
}

void write_disk(u32 lba,u16 *buffer){
     // 1. 等待磁盘准备就绪
     while (inb(0x1F7) & 0x80); // 等待BUSY位清零

     // 2. 发送 LBA 地址和扇区数量
     outb(0x1F2, 1);          // 写入 1 个扇区
     outb(0x1F3, (lba) & 0xFF);
     outb(0x1F4, (lba >> 8) & 0xFF);
     outb(0x1F5, (lba >> 16) & 0xFF);
     outb(0x1F6, ((lba >> 24) & 0x0F) | 0xE0); // LBA 模式

     // 3. 发送写入命令
     outb(0x1F7, 0x30); // 0x30 = WRITE SECTORS

     // 4. 等待磁盘准备好接收数据
     while (!(inb(0x1F7) & 0x08)); // 等待 DRQ = 1，表示可以写入数据

     // 5. 写入 512 字节（256 个 16-bit 数据）
     for (int i = 0; i < 2048; i++) {
         outw(0x1F0, buffer[i]);
     }

     // 6. 发送缓存刷写命令
     outb(0x1F7, 0xE7); // FLUSH CACHE，确保数据写入磁盘
}

